package mysql_sync

import (
	"fmt"
	"gc-rds-ha/internal/common"
	"gc-rds-ha/internal/dao"
	grpcError "gc-rds-ha/internal/error"
	msg "gc-rds-ha/scripts"
	"strings"

	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	"gorm.io/gorm"
)

type MySQLClusterTopology struct {
	dao.DefaultField

	ClusterAlias     string                `gorm:"type:varchar(255);not null;default:'';uniqueIndex:cluster_host_port;index:idx_alias_host;comment:集群别名"`
	InstanceAlias    string                `gorm:"type:varchar(255);not null;default:'';comment:实例别名"`
	Role             string                `gorm:"type:varchar(255);not null;default:'';comment:节点角色"`
	Host             string                `gorm:"type:varchar(255);not null;default:'';uniqueIndex:cluster_host_port;index:idx_alias_host;comment:主机IP"`
	Port             uint32                `gorm:"not null;default:0;uniqueIndex:cluster_host_port;comment:端口号"`
	MgrIsEnable      bool                  `gorm:"type:tinyint(1);not null;default:0;comment:是否启用MGR复制 0否 1是"`
	MgrRole          string                `gorm:"type:varchar(255);not null;default:'';comment:MGR权限"`
	Problems         string                `gorm:"type:varchar(255);not null;default:'';comment:问题"`
	SyncNum          int                   `gorm:"not null;default:1;comment:同步集群拓扑计数"`
	MgrState         int                   `gorm:"type:tinyint(1);not null;default:0;comment:MGR状态 0下线 1上线"`
	IsLastCheckValid msg.EIsLastCheckValid `gorm:"type:tinyint(3);not null;default:1;comment:1上线，2下线"`

	Tx *gorm.DB `gorm:"-"`
}

var (
	tableCountMySQLClusterTopology uint32 = 1 //分表数量
)

func init() {

}

func CreateTableMySQLClusterTopology() {
	d := &MySQLClusterTopology{}
	for i := uint32(0); i < d.TableCount(); i++ {
		if err := dao.DB.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(d); err != nil {
			log.WithField("", log.Fields{"err": err, "tableName": d.TableName()}).Error("DB AutoMigrate Failed")
		}
	}
}

// TableCount 分表数量
func (d *MySQLClusterTopology) TableCount() uint32 {
	return tableCountMySQLClusterTopology
}

// TableName 表名
func (d *MySQLClusterTopology) TableName() string {
	name := "mysql_cluster_topology"
	return name
}

// Model 模型
func (d *MySQLClusterTopology) Model() *gorm.DB {
	db := dao.DB
	if d.Tx != nil {
		db = d.Tx
	}

	return db.Table(d.TableName()).Model(d)
}

// Create 创建
func (d *MySQLClusterTopology) Create(ctx context.Context) error {
	m := d.Model()

	if err := m.Create(d).Error; err != nil {
		log.WithFields(log.Fields{"err": err, "d": d}).Error("m.Create(d) error")
	}

	return nil
}

func BatchCreate(batch []*MySQLClusterTopology) error {
	if err := dao.DB.Create(batch).Error; err != nil {
		log.WithFields(log.Fields{"err": err, "d": batch}).Error("m.Create(d) error")
	}
	return nil
}

func BatchCreateForSql(batchs []*MySQLClusterTopology, tx *gorm.DB) error {
	var (
		t            = common.GetDateTimeInfo("format")
		placeHolders []interface{}
		values       []string
		d            MySQLClusterTopology
	)
	if tx == nil {
		tx = dao.DB
	}
	for _, batch := range batchs {
		values = append(values, "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
		placeHolders = append(placeHolders,
			0, batch.ClusterAlias, batch.InstanceAlias, batch.Role,
			batch.Host, batch.Port, batch.IsLastCheckValid, t, batch.MgrIsEnable, batch.MgrState, batch.MgrRole,
			batch.Problems,
		)
	}
	sql := fmt.Sprintf("insert into %s (`id`, `cluster_alias`, `instance_alias`, `role`, `host`, `port`, `is_last_check_valid`, `created_at`, `mgr_is_enable`, `mgr_state`, `mgr_role`, `problems`) values %s "+
		"on duplicate key update `sync_num`=`sync_num`+1, `updated_at`='%s', `role`= values(role), `mgr_role` = values(mgr_role), `mgr_state` = values(mgr_state), `mgr_is_enable` = values(mgr_is_enable), "+
		"`problems` = values(problems)",
		d.TableName(),
		strings.Join(values, ", "),
		t,
	)
	if err := tx.Exec(sql, placeHolders...).Error; err != nil {
		log.WithFields(log.Fields{
			"sql": sql,
			"err": err,
		}).Error("BatchCreateForSql error")
		return err
	}
	return nil
}

// Get 获取
func (d *MySQLClusterTopology) Get(ctx context.Context) error {
	m := d.Model()

	if d.ID != 0 {
		m = m.Where("id = ?", d.ID)
	}

	if err := m.Order("id asc").First(d).Error; err != nil && err != gorm.ErrRecordNotFound {
		log.WithFields(log.Fields{"err": err, "d": d}).Error("m.Create(d) error")
	}
	return nil
}

func (d *MySQLClusterTopology) List(offset, limit int) ([]*MySQLClusterTopology, error) {
	m := d.Model()
	list := make([]*MySQLClusterTopology, 0)
	if d.ClusterAlias != "" {
		m = m.Where("cluster_alias = ?", d.ClusterAlias)
	}
	if d.IsLastCheckValid != msg.EIsLastCheckValid_ValidUnknown {
		m = m.Where("is_last_check_valid = ?", d.IsLastCheckValid)
	}
	if err := m.Order("id desc").Offset(offset).Limit(limit).Find(&list).Error; err != nil {
		log.WithFields(log.Fields{
			"err:": err,
			"d":    d,
		}).Error("get MySQLClusterTopology List error")
		return list, grpcError.GrpcErrDB
	}
	return list, nil
}
